#pragma once

#include <iostream>
#include <vector>
#include <pthread.h>
#include "Sem.hpp"
#include "Mutex.hpp"

using namespace SemMoudel;
using namespace MutexModule;

namespace RingQueueMoudel
{
    template <class T>
    class RingQueue
    {
    public:
        RingQueue(int cap)
            : _rq(cap), _cap(cap), _p_pos(0), _c_pos(0), _data_sem(0), _space_sem(cap)
        {
        }

        ~RingQueue()
        {
        }

        // 生产者
        void Push(const T &in)
        {
            // 当队满: 阻塞, 直到消费者消费数据
            _space_sem.P(); // 申请空间

            {
                // 先申请信号量, 再申请锁: 此时信号量的申请是并行的, 效率高一点
                LockGuard lockguard(_p_mutex);
                _rq[_p_pos] = in;
                _p_pos++;
                _p_pos %= _cap;
            }

            _data_sem.V(); // 释放数据
        }

        // 消费者
        void Pop(T *out)
        {
            // 当队空: 阻塞, 直到生产者生产数据
            _data_sem.P(); // 申请数据

            {
                // 先申请信号量, 再申请锁: 此时信号量的申请是并行的, 效率高一点
                LockGuard lockguard(_c_mutex);
                *out = _rq[_c_pos];
                _c_pos++;
                _c_pos %= _cap;
            }

            _space_sem.V(); // 释放空间
        }

    private:
        std::vector<T> _rq; // 环型队列, 临界资源
        int _cap;           // 最大容量
        int _p_pos;         // 生产者位置
        int _c_pos;         // 消费者位置

        Sem _data_sem;  // 数据信号量
        Sem _space_sem; // 空间信号量

        Mutex _p_mutex; // 生产者的锁
        Mutex _c_mutex; // 消费者的锁
    };
}